/*
 * 
 *	Copyright (C) 2011 -- Alfred Reibenschuh
 *
 * 	This program is free software; you can redistribute it and/or 
 *	modify it under the terms of the GNU General Public License as 
 *	published by the Free Software Foundation; either version 2 of 
 *	the License, or (at your option) any later version.
 *
 *	This program is distributed in the hope that it will be useful, 
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of 
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 *	GNU General Public License for more details.
 * 
 */

package redoak.php.ldap;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import netscape.ldap.LDAPAttribute;
import netscape.ldap.LDAPAttributeSet;
import netscape.ldap.LDAPConnection;
import netscape.ldap.LDAPConstraints;
import netscape.ldap.LDAPEntry;
import netscape.ldap.LDAPException;
import netscape.ldap.LDAPModification;
import netscape.ldap.LDAPSearchConstraints;
import netscape.ldap.LDAPSearchResults;

public class MozillaLdapSession implements ILdapSession
{
	int port;
	String host;
	String schema;
	LDAPConnection ctx;
	
	public MozillaLdapSession()
	{
	}

	public boolean bindSasl(String u, String p, String mech)
	{
		String uri = schema+"://"+host+":"+port+"/";
		return false;
	}

	public boolean bindSimple(String u, String p)
	{
		try 
		{
			ctx.bind(u, p);
			return true;
		} 
		catch (LDAPException e) 
		{
			e.printStackTrace();
		}

		return false;
	}

	public boolean bind()
	{
		return true;
	}

	public void setPort(int port) 
	{
		this.port=port;
	}
	
	public boolean setUri(String hosturi)
	{
		try 
		{
			URI uri = new URI(hosturi);
			this.schema = uri.getScheme();
			this.host = uri.getHost();
			this.port = uri.getPort();
			if(this.port==0 && this.schema.equals("ldaps"))
			{
				this.port = 636;
			}
			else if(this.port==0)
			{
				this.port = 389;
			}
			return true;
		} 
		catch (URISyntaxException e) 
		{
			e.printStackTrace();
		}
		return false;
	}
	
	public void setHost(String hosturi) 
	{
		this.schema="ldap";
		if(hosturi.indexOf(':')>0)
		{
			this.port = Integer.parseInt(hosturi.substring(hosturi.indexOf(':')+1));
			this.host = hosturi.substring(0,hosturi.indexOf(':'));
		}
		else
		{
			this.host = hosturi;
		}
	}
	
	public boolean close() 
	{
		if(ctx!=null)
		{
			try 
			{
				ctx.disconnect();
				ctx=null;
				return true;
			} 
			catch (LDAPException e) 
			{
				e.printStackTrace();
			}
		}
		return false;
	}

	private NamedHashtable toTable(LDAPEntry attr)
	{
		NamedHashtable v = new NamedHashtable();
		Enumeration en = attr.getAttributeSet().getAttributes();
		while(en.hasMoreElements())
		{
			Vector<String> vv = new Vector();
			LDAPAttribute a = (LDAPAttribute)en.nextElement();
			v.put(a.getName().toLowerCase(), a.getStringValueArray());
		}
		return v;
	}
	
	public NamedHashtable get(String dn) 
	{
		try 
		{
			LDAPEntry attr = ctx.read(dn);
			NamedHashtable t = toTable(attr);
			t.setName(dn);
			return t;
		} 
		catch (LDAPException e) 
		{
			e.printStackTrace();
		}
		return null;
	}

	public NamedHashtable get(String dn, String filter) 
	{
		try 
		{
			LDAPSearchResults ex = ctx.search(dn, LDAPConnection.SCOPE_BASE, filter, new String[]{}, false);
			if(ex.hasMoreElements())
			{
				NamedHashtable v = toTable((LDAPEntry)ex.nextElement());
				v.setName(dn);
				return v;
			}
		} 
		catch (LDAPException e) 
		{
			e.printStackTrace();
		}
		return null;
	}

	public Vector<NamedHashtable> list(String dn, String filter) 
	{
		try 
		{
			Vector<NamedHashtable> vv = new Vector();
			LDAPSearchResults ex = ctx.search(dn, LDAPConnection.SCOPE_ONE, filter, new String[]{}, false);
			while(ex.hasMoreElements())
			{
				NamedHashtable v = toTable((LDAPEntry)ex.nextElement());
				v.setName(dn);
				vv.add(v);
			}
			return vv;
		} 
		catch (LDAPException e) 
		{
			e.printStackTrace();
		}
		return null;
	}

	public Vector<NamedHashtable> search(String dn, String filter) 
	{
		try 
		{
			Vector<NamedHashtable> vv = new Vector();
			LDAPSearchResults ex = ctx.search(dn, LDAPConnection.SCOPE_SUB, filter, new String[]{}, false);
			while(ex.hasMoreElements())
			{
				NamedHashtable v = toTable((LDAPEntry)ex.nextElement());
				v.setName(dn);
				vv.add(v);
			}
			return vv;
		} 
		catch (LDAPException e) 
		{
			e.printStackTrace();
		}
		return null;
	}

	public boolean delete(String dn) 
	{
		try 
		{
			ctx.delete(dn);
			return true;
		} 
		catch (LDAPException e) 
		{
			e.printStackTrace();
		}		
		return false;
	}
	
	public boolean add(NamedHashtable ht) 
	{
		try 
		{
			String name = ht.getName();
			LDAPAttributeSet attrs = new LDAPAttributeSet();
			for(Object key : ht.keySet().toArray())
			{
				LDAPAttribute attr = new LDAPAttribute(key.toString());
				for(String v : ht.get(key))
				{
					attr.addValue(v);
				}
				attrs.add(attr);
			}
			LDAPEntry entry = new LDAPEntry(name,attrs);
			ctx.add(entry);
			return true;
		} 
		catch (LDAPException e) 
		{
			e.printStackTrace();
		}
		return false;
	}
	
	public boolean modify(NamedHashtable ht) 
	{
		try 
		{
			String name = ht.getName();
			NamedHashtable old_attrs = this.get(name);
			Vector<LDAPModification> mods = new Vector();
			
			
			Enumeration<String> en = old_attrs.keys();
			while(en.hasMoreElements())
			{
				String k = en.nextElement();
				if(!ht.containsKey(k))
				{
					mods.add(new LDAPModification(LDAPModification.DELETE, new LDAPAttribute(k)));
				}
			}
			
			for(Object key : ht.keySet().toArray())
			{
				if(old_attrs.get(key.toString())!=null)
				{
					LDAPAttribute attr = new LDAPAttribute(key.toString());
					for(String v : ht.get(key))
					{
						attr.addValue(v);
					}
					mods.add(new LDAPModification(LDAPModification.REPLACE, attr));
				}
			}

			for(Object key : ht.keySet().toArray())
			{
				if(old_attrs.get(key.toString())==null)
				{
					LDAPAttribute attr = new LDAPAttribute(key.toString());
					for(String v : ht.get(key))
					{
						attr.addValue(v);
					}
					mods.add(new LDAPModification(LDAPModification.ADD, attr));
				}
			}
			
			ctx.modify(name, mods.toArray(new LDAPModification[]{}));

			return true;
		} 
		catch (LDAPException e) 
		{
			e.printStackTrace();
		}
		return false;
	}

	public static void main(String[] args)
	{
		MozillaLdapSession ldap = new MozillaLdapSession();
		ldap.setHost("directory.verisign.com:389");
		
		ldap.connect();
		//ldap.bind();
		
		
		NamedHashtable entry = ldap.get("");
		//namingcontexts
		for(String n : entry.get("namingcontexts"))
		{
			System.err.println("ctx="+n);
		}

		entry = ldap.get("dc=verisign,dc=com");
		
		System.err.println(entry);
		
		ldap.close();
	}

	public boolean connect() 
	{
		try 
		{
			ctx = new LDAPConnection();
			ctx.connect(host, port);
			return true;
		} 
		catch (LDAPException e) 
		{
			e.printStackTrace();
		}
		return false;
	}

	public boolean connect(String host, int port) 
	{
		try 
		{
			ctx = new LDAPConnection();
			ctx.connect(host, port);
			return true;
		} 
		catch (LDAPException e) 
		{
			e.printStackTrace();
		}
		return false;
	}
}